跳到主要内容

创建型模式-工厂模式

在工厂模式中,我们在 创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

简单工厂(静态工厂模式)

用来生产同一等级结构中的任意产品(对于增加新的产品,需要扩展已有代码)

弊端:再增加一个新产品需要修改代码(违法了开闭源法则)

public static Car getCar(String car){
if(car.equals("五菱")){
return new WuLing();
}else if(car.equals("特斯拉")){
return new Tesla();
}else{
return null;
}
}

使用反射来改进简单工厂

public Human createHuman(Class<? extends Human> c) {
Human human = null;
try {
human = (Human) class.forName(c.getName()).newInstance();
} catch(Exception e) {
...
}
return human;
}

工厂方法模式

参考资料 JAVA工厂方法的疑问(为何要创建工厂类或者工厂接口)?

classDiagram 加法工厂 ..> 加法类 减法工厂 ..> 减法类 乘法工厂 ..> 乘法类 除法工厂 ..> 除法类 抽象工厂 ..> 运算类 抽象工厂 <|-- 加法工厂 抽象工厂 <|-- 减法工厂 抽象工厂 <|-- 乘法工厂 抽象工厂 <|-- 除法工厂 加法类 --|> 运算类 减法类 --|> 运算类 乘法类 --|> 运算类 除法类 --|> 运算类 运算类 : +NumberA double 运算类 : +NumberB double 运算类: +getResult() double 加法类: +getResult() double 减法类: +getResult() double 乘法类: +getResult() double 除法类: +getResult() double 抽象工厂: <<interface>> 抽象工厂: +createOperation() 运算类

工厂方法里:各个实例工厂封装了实例化产品类的细节,当需要更换不同产品时 只需在客户端更改一处代码 就能创建出不同的产品(这里可以使用反射来避免分支判断),所以可以说工厂方法客服了简单工厂违背开放封闭原则的缺点(如果是使用了反射的简单工厂,则是克服了它无法使用不同的方式去实例化对象的缺点),又保持了封装对象创建过程的优点

// 如果需要变更产品只需更变这个 ConcreteCreatorA
Creator factory = new ConcreteCreatorA();
// 取得产品类
Product product = factory.createProductA();

问题:为什么不直接使用反射创建产品类,还需要单独创建一个工厂来创建产品类 ?

很多回答都是工厂方法是简单工厂的升级版,个人觉得这是不对的,实际上工厂方法面向的使用场景与简单工厂面向的场景有点不一样,简单工厂创建的产品类是一个已经封装好内容,无需外界变动,例如加密类的不同实现,而工厂方法创建的对象大部分则是由各种小单元组成的(组合优于继承)

如下所示的迷宫类

class Maze {
//成员字段
Room r1;
Room r2;
... ...
Wall w1;
Wall w2;
... ...
Door d1;
Door d2;
... ...

//构造器
Maze(){
//按顺序摆放成员字段组件
... ...
}
}

生成一个迷宫,可能需要成百上千个门,墙,房组件,而且还要遵循一定的逻辑规则,因此构造器就会很复杂。更要命的是,迷宫有无数种,如果给每个迷宫都创建一个由上百个组件构成的实体类,然后再给出具体构造流程,那就累死了。

这种情况下,合理的办法是写一个随机迷宫生成器,能根据具体要求不同生成无数种千奇百怪的迷宫,而不是写个死板的迷宫放在那里。这就是创建型模式的意义所在。

这时工厂类就派上用场了,可以在工厂类里面配置具体的生成条件,让这个工厂类组装出一个迷宫实体类(注意不要和建造者模式搞混了),这是简单工厂(使用了反射的那种)无法做到的

抽象工厂模式

参考资料 抽象工厂模式

抽象工厂模式(Abstract Factory),顾名思义,就是把工厂也变成接口,它提供一个创建 一系列 相关或相互依赖对象的接口,而无需指定它们具体的类

classDiagram AbstractFactory <|-- ConcreteFactory1 AbstractFactory <|-- ConcreteFactory2 ConcreteFactory1 ..> ProductA1 ConcreteFactory2 ..> ProductA2 ConcreteFactory1 ..> ProductB1 ConcreteFactory2 ..> ProductB2 ProductA1 --|> AbstractProductA ProductA2 --|> AbstractProductA ProductB1 --|> AbstractProductB ProductB2 --|> AbstractProductB Client --> AbstractFactory AbstractProductA: <<abstract>> AbstractProductB: <<abstract>> AbstractFactory: <<abstract>> AbstractFactory: +createProductA() AbstractFactory: +createProductB() ConcreteFactory1: +createProductA() ConcreteFactory1: +createProductB() ConcreteFactory2: +createProductA() ConcreteFactory2: +createProductB()

如图所示(借用上面文章的图),抽象工厂用于解决纵向拓展的问题(如图所示的 “风格”),抽象工厂可以避免添加新风格时去修改核心代码,添加一组风格的家具只需创建一个 “风格” 工厂,就能完成对不同风格家具的拓展

problem-zh.png

但是添加横向的 “产品族” 就很麻烦了,例如要增加一个台灯,那至少增加三个类:AbstractProductC、ProductC1、ProductC2(以上面类图为例)且需要修改三个类:AbstractFactory、ConcreteFactory1、ConcreteFactory2;综上,添加一个位于产品族的类代价还是挺大的,同时这样也 不满足开放封闭原则 因此抽象工厂适用于产品族固定的场景